﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Devonline.Core;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.Distributed;

namespace Devonline.Identity
{
    public class DefaultKeyRing : ILookupProtectorKeyRing
    {
        private readonly object _locker;
        private readonly List<DefaultKey> _keyRings;
        private readonly DataProtectionOptions _options;
        private readonly IDistributedCache _cache;

        /// <summary>
        /// 存储于分布式缓存里的钥匙串
        /// </summary>
        /// <param name="cache"></param>
        public DefaultKeyRing(DataProtectionOptions options, IDistributedCache cache)
        {
            _options = options;
            _cache = cache;
            var json = string.Empty;

            lock (_locker)
            {
                json = _cache.GetString(_options.KeyName);
            }

            if (json.IsNotNullOrWhiteSpace())
            {
                _keyRings = json.ToJsonObject<List<DefaultKey>>();
            }
        }

        /// <summary>
        /// Return a specific key
        /// </summary>
        /// <param name="keyId">The id of the key to fetch</param>
        /// <returns>The key ring</returns>
        public string this[string keyId] => _keyRings.FirstOrDefault(x => x.Id == keyId)?.Key;

        /// <summary>
        /// Get the current key id.
        /// </summary>
        public string CurrentKeyId => NewestActivationKey();

        /// <summary>
        /// Return all of the key ids.
        /// </summary>
        /// <returns>All of the key ids</returns>
        public IEnumerable<string> GetAllKeyIds() => _keyRings.Select(x => x.Id);

        /// <summary>
        /// get newest key, if not exist, create new
        /// </summary>
        /// <returns></returns>
        private string NewestActivationKey()
        {
            var now = DateTime.Now;
            var key = _keyRings.Where(x => x.CreateTime <= now && x.ExpireTime >= now).OrderByDescending(x => x.CreateTime).FirstOrDefault();
            if (key == null)
            {
                // 尚不存在就创建出来
                key = new DefaultKey
                {
                    Id = KeyGenerator.GetKey<string>(),
                    Key = Guid.NewGuid().ToString().GetHashCode<System.Security.Cryptography.SHA256>(),
                    CreateTime = now,
                    ExpireTime = now.AddDays(_options.ExpireDay),
                };

                _keyRings.Add(key);

                Task.Run(async () =>
                {
                    var json = string.Empty;
                    lock (_locker)
                    {
                        json = _keyRings.ToJsonString();
                    }

                    await _cache.SetStringAsync(_options.KeyName, json);
                });
            }

            return key.Id;
        }
    }
}
